using PK.Broadcaster;
using System;
using System.Collections.Generic;
using UnityEngine;
using VoxelEngine;
using VoxelEngine.Events;

namespace VoxelGame.Character
{
	public class CharacterSkills : MonoBehaviour, IMessageSubscriber<ChangeToolMessage>, IMessageSubscriber<ChangeBlockMessage>
	{
		[Header("Char Controller")]
		[SerializeField]
		protected CharacterSoundController _soundController;

		[SerializeField]
		protected GameObject _faceMarker;

		[SerializeField]
		protected VoxelInputEvents _characterEvents;

		[SerializeField]
		protected LongPressFeedBackController _longPressFeedBackController;

		[SerializeField]
		protected float _rayCastLength = 10f;

		[SerializeField]
		private CharacterToolsController _toolsControler;

		private BlockType _currentBlockSelected;

		protected VoxelEngine.World _world;

		protected RaycastHit _hit;

		protected Block _nearestNeighborBlock;

		protected readonly List<IBlockListener> _blockListeners = new List<IBlockListener>();

		protected bool _processLongPress;

		protected Material _faceMakerMaterial;

		protected const float _minDistanceToCreateBlock = 1.8f;

		protected Vector3 _selectedBlockPosition;

		protected Vector3 _colisionPoint;

		public Action RespawnPlayer;

		private BlockVolume _blockVolume;

		private float ToolRange => _toolsControler.CurrentTool.Range;

		public CharacterToolsController.ToolData.Tools CurrentToolSelected => _toolsControler.CurrentTool.ToolType;

		public void Init(VoxelEngine.World world)
		{
			_world = world;
		}

		private void Awake()
		{
			MessageBroadcaster.Instance.Subscribe<ChangeBlockMessage>(this);
			MessageBroadcaster.Instance.Subscribe<ChangeToolMessage>(this);
		}

		private void Start()
		{
			_characterEvents.OnTap += CreateBlockCallback;
			_characterEvents.OnLongPress += LongPressCallback;
			_characterEvents.OnLongPressProgress += LongPressProgress;
			_characterEvents.OnDragEvent += DragCallback;
			_faceMakerMaterial = _faceMarker.GetComponent<Renderer>().material;
			SetupCharacterTool();
			UpdateCurrentBlockUsed(BlockType.Grass);
		}

		private void OnDestroy()
		{
			MessageBroadcaster.Instance.Unsubscribe<ChangeBlockMessage>(this);
			MessageBroadcaster.Instance.Unsubscribe<ChangeToolMessage>(this);
		}

		private void Update()
		{
			Vector3 position = base.transform.position;
			if (position.y <= 0f)
			{
				RespawnPlayer.Fire();
			}
			UpdateToolSelection();
		}

		private void UpdateToolSelection()
		{
			if (UnityEngine.Input.GetKeyDown(KeyCode.Alpha1))
			{
				_toolsControler.EnableTool(CharacterToolsController.ToolData.Tools.None);
			}
			else if (UnityEngine.Input.GetKeyDown(KeyCode.Alpha2))
			{
				_toolsControler.EnableTool(CharacterToolsController.ToolData.Tools.Pickaxe);
			}
			else if (UnityEngine.Input.GetKeyDown(KeyCode.Alpha3))
			{
				_toolsControler.EnableTool(CharacterToolsController.ToolData.Tools.TNT);
			}
			else if (UnityEngine.Input.GetKeyDown(KeyCode.Alpha4))
			{
				_toolsControler.EnableTool(CharacterToolsController.ToolData.Tools.Drill);
			}
			else if (UnityEngine.Input.GetKeyDown(KeyCode.Alpha5))
			{
				_toolsControler.EnableTool(CharacterToolsController.ToolData.Tools.Portal);
			}
		}

		protected void SetupCharacterTool()
		{
			_toolsControler.SetupTools(OnToolChanged, _soundController);
		}

		private void OnToolChanged(CharacterToolsController.ToolData.Tools type)
		{
			MessageBroadcaster.Instance.BroadcastMessage(new ToolChangedMessage(type));
		}

		protected virtual void LongPressProgress(float progress)
		{
			_longPressFeedBackController.UpdateProgress(progress);
		}

		public void RegisterBlockListener(IBlockListener blockListener)
		{
			_blockListeners.Add(blockListener);
		}

		public void UnregisterBlockListener(IBlockListener blocklistener)
		{
			_blockListeners.Remove(blocklistener);
		}

		private void NotifyBlockCreated(BlockType type)
		{
			foreach (IBlockListener blockListener in _blockListeners)
			{
				blockListener.OnBlockCreated(type);
			}
		}

		private void NotifyBlockRemoved(BlockType type)
		{
			foreach (IBlockListener blockListener in _blockListeners)
			{
				blockListener.OnBlockRemoved(type);
			}
		}

		private void NotifyMultipleBlocksRemoved(Dictionary<BlockType, int> blockDestroyCount)
		{
			foreach (IBlockListener blockListener in _blockListeners)
			{
				blockListener.OnMultipleBlocksRemoved(blockDestroyCount);
			}
		}

		private void CreateBlockCallback(Vector2 position)
		{
			MessageBroadcaster.Instance.BroadcastMessage(new UsingToolMessage());
			CreateBlock(position);
		}

		protected virtual void LongPressCallback(VoxelInputEvents.LongPressState state, Vector2 position)
		{
			if (state != 0 && !_processLongPress)
			{
				return;
			}
			MessageBroadcaster.Instance.BroadcastMessage(new UsingToolMessage());
			switch (state)
			{
			case VoxelInputEvents.LongPressState.Begin:
				if (_toolsControler.CurrentTool.CanStartOperation() || _toolsControler.CurrentTool.ToolType == CharacterToolsController.ToolData.Tools.Pickaxe)
				{
					_processLongPress = true;
				}
				break;
			case VoxelInputEvents.LongPressState.Detected:
				if (!_toolsControler.CurrentTool.NeedValidBlockTargetBeforeStart() || IsCurrentBlockValid(position))
				{
					_longPressFeedBackController.Show(position);
					_toolsControler.CurrentTool.PlayLongPressBeginAnimation();
				}
				break;
			case VoxelInputEvents.LongPressState.Failed:
				_longPressFeedBackController.Hide();
				_toolsControler.CurrentTool.StopLongPressBeginAnimation();
				break;
			case VoxelInputEvents.LongPressState.Hold:
				_longPressFeedBackController.Move(position);
				if (_toolsControler.CurrentTool.CanStartOperation())
				{
					if (IsCurrentBlockValid(position))
					{
						DestroyBlock(position);
						if (!_toolsControler.CurrentTool.CanContinuousDestroy())
						{
							LongPressCallback(VoxelInputEvents.LongPressState.End, Vector2.zero);
						}
					}
				}
				else
				{
					LongPressProgress(_toolsControler.CurrentTool.AnimationProgress());
				}
				break;
			case VoxelInputEvents.LongPressState.End:
				_processLongPress = false;
				_longPressFeedBackController.Hide();
				_toolsControler.CurrentTool.StopLongPressBeginAnimation();
				break;
			}
		}

		protected bool IsCurrentBlockValid(Vector2 position)
		{
			RaycastHit hit;
			Block block = GetBlock(out hit, position);
			return block != null && (block.IsSolid() || block.Type == BlockType.Water) && block.Type != BlockType.Unknown;
		}

		private void DragCallback(Vector2 position)
		{
			MessageBroadcaster.Instance.BroadcastMessage(new UsingToolMessage());
			Block block = GetBlock(out _hit, position);
			if (block != null && block.Type != BlockType.Unknown)
			{
				HighlightCubeFace(block);
			}
		}

		private void FadeHighlight()
		{
			Color color = _faceMakerMaterial.GetColor("_MainColor");
			if (color.a >= 0f)
			{
				_faceMakerMaterial.SetColor("_MainColor", new Color(color.r, color.g, color.b, color.a - 0.05f));
				return;
			}
			CancelInvoke("FadeHighlight");
			_faceMarker.transform.position = Vector3.zero;
		}

		protected virtual void HighlightCubeFace(Block referenceBlock)
		{
			CancelInvoke("FadeHighlight");
			_faceMakerMaterial.SetColor("_MainColor", new Color(1f, 1f, 1f, 1f));
			Vector3 vector = new Vector3(referenceBlock._wx, referenceBlock._wy, referenceBlock._wz);
			_faceMarker.transform.position = new Vector3(vector.x + 0.5f, vector.y + 0.5f, vector.z + 0.5f);
			InvokeRepeating("FadeHighlight", 1f, 0.02f);
		}

		protected virtual void CreateBlock(Vector2 position)
		{
			if (!_toolsControler.CurrentTool.CanCreateBlock || !_toolsControler.CurrentTool.CanStartOperation(waitHalfAnimDuration: true))
			{
				return;
			}
			_nearestNeighborBlock = GetBlock(out _hit, position, getNearestNeighbor: true);
			if (_hit.distance > 1.8f && _nearestNeighborBlock != null)
			{
				BlockType blockType = _currentBlockSelected;
				if (BlockTypes.IsHalfBlock(blockType))
				{
					blockType = BlockTypes.Instance.GetHalfPartByVolume(blockType, _blockVolume);
				}
				if (_toolsControler.CurrentTool.CreateBlock(_world, _nearestNeighborBlock, blockType))
				{
					HighlightCubeFace(_nearestNeighborBlock);
					NotifyBlockCreated(blockType);
				}
			}
		}

		protected virtual void DestroyBlock(Vector2 inputPos)
		{
			if (!_toolsControler.CurrentTool.CanDestroyBlock || !_toolsControler.CurrentTool.CanStartOperation())
			{
				return;
			}
			RaycastHit hit;
			Block block = GetBlock(out hit, inputPos);
			if (block != null && (block.IsSolid() || block.Type == BlockType.Water))
			{
				if (_toolsControler.CurrentTool.CanShootProjectile || !block.Indestructible())
				{
					byte light = GetBlock(out hit, inputPos, getNearestNeighbor: true).Light;
					StartCoroutine(_toolsControler.CurrentTool.DestroyBlock(_world, block, NotifyMultipleBlocksRemoved, light));
				}
			}
			else
			{
				_faceMarker.transform.position = Vector3.zero;
			}
		}

		protected virtual Block GetBlock(out RaycastHit hit, Vector2 direction, bool getNearestNeighbor = false)
		{
			Ray ray = Camera.main.ScreenPointToRay(direction);
			hit = default(RaycastHit);
			Block block = new Block(BlockType.Unknown);
			int num = 1;
			int num2 = 256;
			int layerMask = num | num2;
			if (Physics.Raycast(ray, out hit, ToolRange, layerMask))
			{
				if (hit.transform.gameObject.tag == "NPC")
				{
					return block;
				}
				_colisionPoint = hit.point;
				Vector3 vector = _colisionPoint + 0.0001f * ray.direction;
				int num3 = Mathf.CeilToInt(vector.x) - 1;
				int num4 = Mathf.CeilToInt(vector.y) - 1;
				int num5 = Mathf.CeilToInt(vector.z) - 1;
				_selectedBlockPosition = new Vector3(num3, num4, num5);
				if (getNearestNeighbor)
				{
					Vector3 vector2 = _colisionPoint - _selectedBlockPosition;
					_blockVolume = BlockVolume.Full;
					if (vector2.y < 0.5f)
					{
						_blockVolume = BlockVolume.HalfBottom;
					}
					else if (vector2.y > 0.5f)
					{
						_blockVolume = BlockVolume.HalfTop;
					}
					else if (vector2.y == 0.5f)
					{
						Block block2 = _world[num3, num4, num5];
						if (block2.Volume() == BlockVolume.HalfBottom)
						{
							_blockVolume = BlockVolume.HalfTop;
						}
						else
						{
							_blockVolume = BlockVolume.HalfBottom;
						}
					}
					if (vector2.x == 1f)
					{
						num3++;
					}
					else if (vector2.x == 0f)
					{
						num3--;
					}
					if (vector2.y == 1f)
					{
						num4++;
						_blockVolume = BlockVolume.HalfBottom;
					}
					else if (vector2.y == 0f)
					{
						num4--;
						_blockVolume = BlockVolume.HalfTop;
					}
					if (vector2.z == 1f)
					{
						num5++;
					}
					else if (vector2.z == 0f)
					{
						num5--;
					}
					_selectedBlockPosition.x = num3;
					_selectedBlockPosition.y = num4;
					_selectedBlockPosition.z = num5;
					block = _world[num3, num4, num5];
					if (block.IsCollapsedBlock())
					{
						_blockVolume = BlockVolume.Full;
					}
				}
				else
				{
					block = _world[num3, num4, num5];
				}
			}
			return block;
		}

		public void OnMessageReceived(ChangeToolMessage message)
		{
			_toolsControler.EnableTool(message.ToolType);
		}

		public void OnMessageReceived(ChangeBlockMessage message)
		{
			UpdateCurrentBlockUsed(message.BlockType);
		}

		private void UpdateCurrentBlockUsed(BlockType blockType)
		{
			_currentBlockSelected = blockType;
			MessageBroadcaster.Instance.BroadcastMessage(new BlockChangedMessage(blockType));
		}
	}
}
